package dovs.phases;

import dovs.*;
import dovs.analysis.*;
import dovs.node.*;

import java.util.*;
import java.io.*;

/** 
 *	Compiler phase to weed out unwanted trees produced by the parser
 *	and calculate the value of all constant expressions.
 */
public aspect Weeding extends DepthFirstAdapter {
	/** The int value of an integer literal */
	public int AIntConstExp.value;

	/** The char value of a character literal */
	public int ACharConstExp.value;

	/** The String value of a string literal */
	public String AStringConstExp.value;

	/** The boolean value of a boolean literal */
	public boolean ABooleanConstExp.value;

	@Override
	public void inAIntConstExp(AIntConstExp exp) { 
		// Parse integer literal 
		// TODO: Check that value is within the legal int range 
		exp.value = Integer.parseInt(exp.getIntegerLiteral().getText());
	}

	@Override 
	public void inAConstructorDecl(AConstructorDecl constructord) { 
		// Add implicit super constructor call 
		TIdentifier constructor_pos = constructord.getName(); 
		TSuper pos = new TSuper(constructor_pos.getLine(), constructor_pos.getPos()); 
		ASuperStm super_stm = new ASuperStm(pos, new LinkedList<PExp>());
		constructord.getBody().getBlock().getStatements().addFirst(super_stm);
	}



	// /////////////////////////////////////////////////////////////////////////
	// ERROR MESSAGES
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Report the error that an abstract class cannot be final.
	 * 
	 * @param node
	 *            the class declaration node
	 */
	@SuppressWarnings("unused")
	private static void errorAbstractFinalClass(AClassTypeDecl node) {
		Errors.error(ErrorType.ABSTRACT_FINAL_CLASS, node.getName(),
				"The abstract class " + node.getName().getText()
						+ " cannot be final.", false);
	}

	/**
	 * Report the error that an abstract method has a body.
	 * 
	 * @param node
	 *            the method declaration node
	 */
	@SuppressWarnings("unused")
	private static void errorAbstractMethodBody(AMethodDecl node) {
		Errors.error(ErrorType.ABSTRACT_METHOD_BODY, node.getName(),
				"The abstract method " + Util.getMethodSignatureString(node)
						+ " must not have a body.", false);
	}

	/**
	 * Reports the error that an abstract method cannot be final or static.
	 * 
	 * @param node
	 *            the method declaration
	 */
	@SuppressWarnings("unused")
	private static void errorAbstractMethodFinalOrStatic(AMethodDecl node) {
		Errors.error(ErrorType.ABSTRACT_METHOD_FINAL_OR_STATIC, node.getName(),
				"The abstract method " + Util.getMethodSignatureString(node)
						+ " cannot be static or final.", false);
	}

	/**
	 * Reports the error that a formal parameter cannot have an initializer.
	 * 
	 * @param node
	 *            the local declaration for the formal parameter
	 */
	@SuppressWarnings("unused")
	private static void errorFormalInitializer(ALocalDecl node) {
		Errors.error(ErrorType.FORMAL_INITIALIZER, node.getName(),
				"A formal parameter must not have an initializer.", false);
	}

	/**
	 * Reports the error that an interface must declare no fields.
	 * 
	 * @param node
	 *            the interface declaration
	 */
	@SuppressWarnings("unused")
	private static void errorInterfaceField(AFieldDecl node) {
		Errors.error(ErrorType.INTERFACE_FIELD, node.getName(),
				"An interface must declare no fields.", false);
	}

	/**
	 * Reports the error that an interface must declare no constructors.
	 * 
	 * @param node
	 *            the interface declaration
	 */
	@SuppressWarnings("unused")
	private static void errorInterfaceConstructor(AConstructorDecl node) {
		Errors.error(ErrorType.INTERFACE_CONSTRUCTOR, node.getName(),
				"An interface must declare no constructors.", false);
	}

	/**
	 * Reports the error that an interface method cannot have a body.
	 * 
	 * @param node
	 *            the method declaration
	 */
	@SuppressWarnings("unused")
	private static void errorInterfaceMethodWithBody(AMethodDecl node) {
		Errors.error(ErrorType.INTERFACE_METHOD_WITH_BODY, node.getName(),
				"The interface method must not have a body.", false);
	}

	/**
	 * Reports an error for when an integer literal is out of range.
	 * 
	 * @param node
	 *            the integer literal
	 * @param message
	 *            an error message for the invalid integer
	 */
	@SuppressWarnings("unused")
	private static void errorInvalidInteger(TIntegerLiteral node, String message) {
		Errors.error(ErrorType.INVALID_INTEGER, node, "Integer value "
				+ node.getText() + " is out of range: " + message, false);
	}

	/**
	 * Reports the error that a type declaration must reside in a file by the
	 * same name as the declared class or interface.
	 * 
	 * @param node
	 *            the type declaration
	 */
	@SuppressWarnings("unused")
	private static void errorInvalidSourceFileName(PTypeDecl node) {
		String expected_name = node.getName().getText() + ".java";
		String kind = node instanceof AClassTypeDecl ? "class" : "interface";
		Errors.error(ErrorType.INVALID_SOURCE_FILE_NAME, node.getName(),
				"The public " + kind + " " + node.getName().getText() + " "
						+ "must be declared in a file called " + expected_name,
				false);
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that
	 * explicit super call is used.
	 * 
	 * @param node
	 *            the super call statement
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1ExplicitSuperCall(ASuperStm node) {
		Errors.checkJoos1(ErrorType.JOOS1_EXPLICIT_SUPER_CALL, node.getToken(),
				"explicit super call");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that final
	 * field declaration is used.
	 * 
	 * @param node
	 *            the field declaration
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1FinalFieldDeclaration(AFieldDecl node) {
		Errors.checkJoos1(ErrorType.JOOS1_FINAL_FIELD_DECLARATION,
				node.getName(), "final field declaration");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that the
	 * increment or decrement expression is used.
	 * 
	 * @param node
	 *            the inc/dec expression
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1IncDec(AIncDecExp node) {
		Errors.checkJoos1(ErrorType.JOOS1_INC_DEC,
				node.getIncDecOp().getToken(), "increment and decrement");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that
	 * interface declaration is used.
	 * 
	 * @param node
	 *            the interface declaration
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1Interface(AInterfaceTypeDecl node) {
		Errors.checkJoos1(ErrorType.JOOS1_INTERFACE, node.getName(),
				"interface");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that
	 * multidimensional arrays are used.
	 * 
	 * @param node
	 *            the (multidimensional) array type
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1MultiArray(AArrayType node) {
		Errors.checkJoos1(ErrorType.JOOS1_MULTI_ARRAY, node.getToken(),
				"multidimensional array");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that the
	 * constructor is omitted.
	 * 
	 * @param node
	 *            the class declaration without an explicit constructor
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1OmittedConstructor(AClassTypeDecl node) {
		Errors.checkJoos1(ErrorType.JOOS1_OMITTED_CONSTRUCTOR, node.getName(),
				"omitted constructor");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that
	 * static field declaration is used.
	 * 
	 * @param node
	 *            the field declaration
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1StaticFieldDeclaration(AFieldDecl node) {
		Errors.checkJoos1(ErrorType.JOOS1_STATIC_FIELD_DECLARATION,
				node.getName(), "static field declaration");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that this
	 * call is used.
	 * 
	 * @param node
	 *            the this statement
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1ThisCall(AThisStm node) {
		Errors.checkJoos1(ErrorType.JOOS1_THIS_CALL, node.getToken(),
				"this call");
	}

	/**
	 * If compiling with the {@code -joos1} option, reports the error that throw
	 * is used.
	 * 
	 * @param node
	 *            the throw statement
	 */
	@SuppressWarnings("unused")
	private static void checkJoos1Throw(AThrowStm node) {
		Errors.checkJoos1(ErrorType.JOOS1_THROW, node.getToken(), "throw");
	}

	/**
	 * Reports the error that a final field declaration has no initializer.
	 * 
	 * @param node
	 *            the field declaration
	 */
	@SuppressWarnings("unused")
	private static void errorMissingFinalFieldInitializer(AFieldDecl node) {
		Errors.error(ErrorType.MISSING_FINAL_FIELD_INITIALIZER, node.getName(),
				"A final field must have an initializer", false);
	}

	/**
	 * Reports the error that a nonabstract method must have a body.
	 * 
	 * @param node
	 *            the method declaration
	 */
	@SuppressWarnings("unused")
	private static void errorNonAbstractMethodBody(AMethodDecl node) {
		Errors.error(ErrorType.NON_ABSTRACT_METHOD_BODY, node.getName(),
				"A non-abstract method must have a body.", false);
	}

	/**
	 * Reports the error that a static method cannot be final.
	 * 
	 * @param node
	 *            the method declaration
	 */
	@SuppressWarnings("unused")
	private static void errorStaticFinalMethod(AMethodDecl node) {
		Errors.error(ErrorType.STATIC_FINAL_METHOD, node.getName(),
				"A static method cannot be final.", false);
	}

	/**
	 * Reports the error that an interface method cannot be static or final.
	 * 
	 * @param node
	 *            the method declaration
	 */
	@SuppressWarnings("unused")
	private static void errorStaticOrFinalInterfaceMethod(AMethodDecl node) {
		Errors.error(ErrorType.STATIC_OR_FINAL_INTERFACE_METHOD,
				node.getName(),
				"An interface method cannot be static or final.", false);
	}

	/**
	 * Reports the error that a super call statement was not the first statement
	 * in the constructor.
	 * 
	 * @param node
	 *            the super call statement
	 */
	@SuppressWarnings("unused")
	private static void errorSuperCallNotFirstStatement(ASuperStm node) {
		Errors.error(ErrorType.SUPER_CALL_NOT_FIRST_STATEMENT, node.getToken(),
				"Super constructor call must be the first statement "
						+ "in a constructor body", false);
	}

	/**
	 * Reports a syntax error for syntax handled in the weeder.
	 * 
	 * @param pos
	 *            the position at which the error occurred
	 * @param message
	 *            an error message for the syntax error
	 */
	@SuppressWarnings("unused")
	private static void errorSyntaxError(Token pos, String message) {
		Errors.error(ErrorType.SYNTAX_ERROR, pos, message, false);
	}

	/**
	 * Reports the error that a this statement is not the first statement in the
	 * constructor.
	 * 
	 * @param node
	 *            the this statement
	 */
	@SuppressWarnings("unused")
	private static void errorThisCallNotFirstStatement(AThisStm node) {
		Errors.error(
				ErrorType.THIS_CALL_NOT_FIRST_STATEMENT,
				node.getToken(),
				"Constructor call must be the first statement in a constructor body",
				false);
	}

	/**
	 * Reports the error that an array element type cannot be void.
	 * 
	 * @param node
	 *            the array type
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeArray(AArrayType node) {
		Errors.error(ErrorType.VOID_TYPE_ARRAY, node.getToken(),
				"Array element type cannot be void", false);
	}

	/**
	 * Reports the error that a field cannot have type void.
	 * 
	 * @param node
	 *            the field declaration
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeField(AFieldDecl node) {
		Errors.error(ErrorType.VOID_TYPE_FIELD, node.getName(),
				"Fields cannot have void type", false);
	}

	/**
	 * Reports the error that the type of an instanceof expression cannot be
	 * void.
	 * 
	 * @param node
	 *            the instanceof expression
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeInstanceof(AInstanceofExp node) {
		Errors.error(ErrorType.VOID_TYPE_INSTANCEOF, node.getToken(),
				"Instanceof type cannot be void", false);
	}

	/**
	 * Reports the error that a variable cannot have type void.
	 * 
	 * @param node
	 *            the variable declaration
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeVariable(ALocalDecl node) {
		Errors.error(ErrorType.VOID_TYPE_VARIABLE, node.getName(),
				"Variables cannot have void type", false);
	}

	/**
	 * Reports the error that void can only be used as return type.
	 * 
	 * @param node
	 *            the void type
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeNotReturnType(AVoidType node) {
		Errors.error(ErrorType.VOID_TYPE_NOT_RETURN_TYPE, node.getToken(),
				"void can only be used as return type", false);
	}

	/**
	 * Reports the error that void can not used as cast type.
	 * 
	 * @param pos
	 *            the position at which the error occured
	 */
	@SuppressWarnings("unused")
	private static void errorVoidTypeCast(Token pos) {
		Errors.error(ErrorType.VOID_TYPE_CAST, pos,
				"Cast to void type is illegal", false);
	}

	/**
	 * Reports the error that a constructor does not have the same name as its
	 * enclosing class.
	 * 
	 * @param constructor
	 *            the constructor declaration
	 */
	@SuppressWarnings("unused")
	private static void errorConstructorName(AConstructorDecl constructor) {
		Errors.error(ErrorType.CONSTRUCTOR_NAME, constructor.getName(),
				"Constructor must have the same name as its enclosing class",
				false);
	}
}
